---
title: PdfAPI.SyncFusionCore business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-09-17
library: PdfAPI.SyncFusionCore
reference_type: package
---

# PdfAPI.SyncFusionCore business rules

This page captures the 39 business rules enforced by `PdfAPI.SyncFusionCore`. This library has no database interactions, so no rule carries a Tables or SQL field.

## BR-SFC-001 Null conversion request rejected

| Field | Value |
| --- | --- |
| **Statement** | A conversion request must not be null. |
| **Trigger** | Entry to the conversion factory. |
| **Enforcement** | `ConverterFactory.Convert` throws `ArgumentNullException(nameof(request))`. |
| **Outcome** | `ArgumentNullException` thrown; no converter is invoked. |
| **Source** | `PdfApi.SyncFusionCore\Convert\ConverterFactory.cs` |

---

## BR-SFC-002 Case-insensitive extension normalization

| Field | Value |
| --- | --- |
| **Statement** | File extensions are matched case-insensitively and without leading/trailing dots. |
| **Trigger** | Before extension dispatch. |
| **Enforcement** | `ConverterFactory.Convert` lower-cases and trims `request.FileExtension`. |
| **Outcome** | `FileExtension` normalized in place on the request. |
| **Source** | `PdfApi.SyncFusionCore\Convert\ConverterFactory.cs` |

---

## BR-SFC-003 Extension to converter mapping

| Field | Value |
| --- | --- |
| **Statement** | Each supported extension maps to exactly one converter: `doc`/`docx`→Word, `xls`/`xlsx`→Excel, `rtf`→RTF, `txt`→Text, `gif`/`jpg`/`jpeg`/`png`→Image, `html`→HTML, `tif`/`tiff`→TIFF, `pdf`→PDF. |
| **Trigger** | Extension dispatch. |
| **Enforcement** | `ConverterFactory.Convert` `switch` statement. |
| **Outcome** | Matching converter invoked. |
| **Source** | `PdfApi.SyncFusionCore\Convert\ConverterFactory.cs` |

---

## BR-SFC-004 Text converted via intermediate PDF

| Field | Value |
| --- | --- |
| **Statement** | A text file is always converted to an intermediate PDF first, then re-submitted through the PDF path so that the requested output format (including PDF/A) is applied. |
| **Trigger** | `txt` conversion succeeded. |
| **Enforcement** | `ConverterFactory.Convert` (`txt` case) recursively calls itself with `FileExtension = "pdf"`. |
| **Outcome** | Second, recursive conversion replaces the first result. |
| **Source** | `PdfApi.SyncFusionCore\Convert\ConverterFactory.cs`, `PdfApi.SyncFusionCore\Convert\TxtConverter.cs` |

---

## BR-SFC-005 Unsupported extension reported not thrown

| Field | Value |
| --- | --- |
| **Statement** | An unsupported extension is reported as a result carrying `SourceConversionType.Unknown` and the message `Unsuppported extension ({ext})`, not as an exception. |
| **Trigger** | No switch branch matched. |
| **Enforcement** | `ConverterFactory.Convert` default case. |
| **Outcome** | Non-throwing result returned; `Status` left at its default value (`NotTracked`) rather than `Failed`. |
| **Source** | `PdfApi.SyncFusionCore\Convert\ConverterFactory.cs` |

---

## BR-SFC-006 Result stamped Syncfusion processor

| Field | Value |
| --- | --- |
| **Statement** | Results produced by `BaseConverter.Convert` and `PdfMerger.MergeDocumentRanges` are stamped `PdfProcessorType.Syncfusion`; the unmatched-extension result constructed directly by `ConverterFactory.Convert` is not stamped and keeps the default `PdfProcessorType`. |
| **Trigger** | Result construction inside `BaseConverter.Convert` or `PdfMerger.MergeDocumentRanges`. |
| **Enforcement** | `BaseConverter.Convert`; `PdfMerger.MergeDocumentRanges`. Not enforced by `ConverterFactory.Convert`'s unmatched-extension branch, which builds a `PdfResult` directly. |
| **Outcome** | Processor provenance recorded on results from an actual conversion or merge; results from an unrecognized extension carry no processor stamp. |
| **Source** | `PdfApi.SyncFusionCore\Convert\BaseConverter.cs`, `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-007 Conversion success requires non-null document

| Field | Value |
| --- | --- |
| **Statement** | A conversion succeeds only when the converter produced a non-null document file; otherwise it is marked failed. |
| **Trigger** | After `ConvertItem` returns. |
| **Enforcement** | `BaseConverter.Convert` checks `Document?.File`. |
| **Outcome** | `PdfStatus.Succeeded` or `PdfStatus.Failed`. |
| **Source** | `PdfApi.SyncFusionCore\Convert\BaseConverter.cs` |

---

## BR-SFC-008 Output format reflects PDF/A option

| Field | Value |
| --- | --- |
| **Statement** | The result's output format reflects the requested PDF/A option. |
| **Trigger** | Result assembly. |
| **Enforcement** | `BaseConverter.Convert`. |
| **Outcome** | `OutputFormat = PdfA` when `Options.IsPdfa`, else `Pdf`. |
| **Source** | `PdfApi.SyncFusionCore\Convert\BaseConverter.cs` |

---

## BR-SFC-009 Source system id parsed to numeric image id

| Field | Value |
| --- | --- |
| **Statement** | The source system id is carried onto the result as a numeric image id; a null or non-numeric value yields `0` rather than an error. |
| **Trigger** | Result assembly. |
| **Enforcement** | `BaseConverter.Convert`; `PdfMerger.MergeDocumentRanges` — both use `long.TryParse`. |
| **Outcome** | `ImageId` set; no failure on bad input. |
| **Source** | `PdfApi.SyncFusionCore\Convert\BaseConverter.cs`, `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-010 Conversion exceptions captured not rethrown

| Field | Value |
| --- | --- |
| **Statement** | Any exception raised during a single-document conversion is captured as a failed result carrying the exception message; it is never rethrown and never logged. |
| **Trigger** | Exception inside `BaseConverter.Convert`. |
| **Enforcement** | `BaseConverter.Convert` `catch` block. |
| **Outcome** | `Status = Failed`, `Message = ex.Message`; caller sees no exception. |
| **Source** | `PdfApi.SyncFusionCore\Convert\BaseConverter.cs` |

---

## BR-SFC-011 PDF/A conformance level A1B

| Field | Value |
| --- | --- |
| **Statement** | When PDF/A output is requested, the conversion is performed at conformance level PDF/A-1b. |
| **Trigger** | `Options.IsPdfa` (or `Format == PdfA` in the HTML converter) is true. |
| **Enforcement** | `WordConverter`, `ExcelConverter`, `RtfConverter`, `ImageConverter`, `TiffConverter`, `HtmlConverter`, `PdfConverter`, `PdfExtractor` (`ConvertItem` / `Extract`). |
| **Outcome** | `PdfConformanceLevel.Pdf_A1B` applied. |
| **Source** | `PdfApi.SyncFusionCore\Convert\WordConverter.cs`, `PdfApi.SyncFusionCore\Convert\ExcelConverter.cs`, `PdfApi.SyncFusionCore\Convert\RtfConverter.cs`, `PdfApi.SyncFusionCore\Convert\ImageConverter.cs`, `PdfApi.SyncFusionCore\Convert\TiffConverter.cs`, `PdfApi.SyncFusionCore\Convert\HtmlConverter.cs`, `PdfApi.SyncFusionCore\Convert\PdfConverter.cs`, `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs` |

---

## BR-SFC-012 ASCII-only source text reading

| Field | Value |
| --- | --- |
| **Statement** | RTF and plain-text sources are read as ASCII; characters outside ASCII are not preserved. |
| **Trigger** | Source read. |
| **Enforcement** | `RtfConverter.ConvertItem`, `TxtConverter.ConvertItem` — `StreamReader(stream, Encoding.ASCII)`. |
| **Outcome** | Non-ASCII characters are not preserved in the output. |
| **Source** | `PdfApi.SyncFusionCore\Convert\RtfConverter.cs`, `PdfApi.SyncFusionCore\Convert\TxtConverter.cs` |

---

## BR-SFC-013 RTF text flows across pages

| Field | Value |
| --- | --- |
| **Statement** | RTF content flows across page boundaries without a forced break. |
| **Trigger** | RTF metafile layout. |
| **Enforcement** | `RtfConverter.ConvertItem` sets `PdfMetafileLayoutFormat.SplitTextLines = true`. |
| **Outcome** | Text splits naturally across pages instead of being clipped. |
| **Source** | `PdfApi.SyncFusionCore\Convert\RtfConverter.cs` |

---

## BR-SFC-014 Fixed 40 lines per page pagination

| Field | Value |
| --- | --- |
| **Statement** | Plain text is paginated at a fixed 40 lines per page (overridable per instance through the public `LinesPerPage` field). |
| **Trigger** | Text pagination loop. |
| **Enforcement** | `TxtConverter.LinesPerPage`, `TxtConverter.ConvertItem`. |
| **Outcome** | One page emitted per 40-line block until the text is exhausted. |
| **Source** | `PdfApi.SyncFusionCore\Convert\TxtConverter.cs` |

---

## BR-SFC-015 Fixed text typography

| Field | Value |
| --- | --- |
| **Statement** | Plain text is rendered in 12 pt Helvetica, justified, top-aligned, with a 15 pt paragraph indent. |
| **Trigger** | Text page rendering. |
| **Enforcement** | `TxtConverter.ConvertItem`. |
| **Outcome** | Fixed typography applied to every page. |
| **Source** | `PdfApi.SyncFusionCore\Convert\TxtConverter.cs` |

---

## BR-SFC-016 Single image produces single page

| Field | Value |
| --- | --- |
| **Statement** | A single raster image produces a single-page PDF. |
| **Trigger** | Image conversion. |
| **Enforcement** | `ImageConverter.ConvertItem`. |
| **Outcome** | Exactly one page added. |
| **Source** | `PdfApi.SyncFusionCore\Convert\ImageConverter.cs` |

---

## BR-SFC-017 Image and frame stretched to full page

| Field | Value |
| --- | --- |
| **Statement** | Images and TIFF frames are stretched to the full page client area; the source aspect ratio is not preserved. |
| **Trigger** | Image/frame draw. |
| **Enforcement** | `ImageConverter.ConvertItem`, `TiffConverter.ConvertItem` — `DrawImage` with the full page rectangle. |
| **Outcome** | Source aspect ratio not preserved in the output. |
| **Source** | `PdfApi.SyncFusionCore\Convert\ImageConverter.cs`, `PdfApi.SyncFusionCore\Convert\TiffConverter.cs` |

---

## BR-SFC-018 TIFF pages have zero margins

| Field | Value |
| --- | --- |
| **Statement** | TIFF output pages carry zero margins. |
| **Trigger** | TIFF document setup. |
| **Enforcement** | `TiffConverter.ConvertItem` sets `PageSettings.Margins.All = 0`. |
| **Outcome** | No margin reserved on TIFF-converted pages. |
| **Source** | `PdfApi.SyncFusionCore\Convert\TiffConverter.cs` |

---

## BR-SFC-019 One PDF page per TIFF frame

| Field | Value |
| --- | --- |
| **Statement** | A multi-frame TIFF produces one PDF page per frame, in frame order. |
| **Trigger** | TIFF frame loop. |
| **Enforcement** | `TiffConverter.ConvertItem`. |
| **Outcome** | Page count equals `FrameCount`. |
| **Source** | `PdfApi.SyncFusionCore\Convert\TiffConverter.cs` |

---

## BR-SFC-020 PDF/A applied via reload and resave

| Field | Value |
| --- | --- |
| **Statement** | A PDF/A request applies the conformance level by reloading the finished document and re-saving it. |
| **Trigger** | `Options.IsPdfa` true. |
| **Enforcement** | `PdfConverter.ConvertItem`; `PdfExtractor.Extract`. |
| **Outcome** | Conformance-upgraded bytes returned. |
| **Source** | `PdfApi.SyncFusionCore\Convert\PdfConverter.cs`, `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs` |

---

## BR-SFC-021 Plain PDF returned unchanged

| Field | Value |
| --- | --- |
| **Statement** | A PDF source requested as plain PDF is returned byte-for-byte unchanged. |
| **Trigger** | Extension is `pdf` and PDF/A not requested. |
| **Enforcement** | `PdfConverter.ConvertItem`. |
| **Outcome** | Input stream copied verbatim. |
| **Source** | `PdfApi.SyncFusionCore\Convert\PdfConverter.cs` |

---

## BR-SFC-022 HTML rendered with IE engine

| Field | Value |
| --- | --- |
| **Statement** | HTML is rendered with the Internet Explorer engine. |
| **Trigger** | HTML conversion. |
| **Enforcement** | `HtmlConverter.ConvertItem` uses `IEConverterSettings`. |
| **Outcome** | HTML rendered through the legacy IE rendering engine, in-process. |
| **Source** | `PdfApi.SyncFusionCore\Convert\HtmlConverter.cs` |

---

## BR-SFC-023 HTML converted with empty base URL

| Field | Value |
| --- | --- |
| **Statement** | HTML is converted with an empty base URL, so relative resource references are not resolved. |
| **Trigger** | HTML conversion. |
| **Enforcement** | `HtmlConverter.ConvertItem` calls `converter.Convert(html, "")`. |
| **Outcome** | Relative resource references in the source HTML do not resolve. |
| **Source** | `PdfApi.SyncFusionCore\Convert\HtmlConverter.cs` |

---

## BR-SFC-024 Full document range returned unchanged

| Field | Value |
| --- | --- |
| **Statement** | When a document is requested in full (a single original `1`–`*` range), the original file is returned as-is without re-processing — including when PDF/A was requested. |
| **Trigger** | Extraction entry. |
| **Enforcement** | `PdfExtractor.Extract`. |
| **Outcome** | Source bytes returned unchanged. |
| **Source** | `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs` |

---

## BR-SFC-025 Out-of-range pages clamped or skipped

| Field | Value |
| --- | --- |
| **Statement** | Page ranges beyond the end of the document are clamped to the last page, and a range that lies entirely beyond the last page is skipped. |
| **Trigger** | Per range, during extraction. |
| **Enforcement** | `PdfExtractor.Extract`. |
| **Outcome** | Out-of-range requests degrade silently instead of failing. |
| **Source** | `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs` |

---

## BR-SFC-026 Open-ended stop page means last page

| Field | Value |
| --- | --- |
| **Statement** | An open-ended stop page (`-1`) means "through the last page". |
| **Trigger** | Per range, during extraction. |
| **Enforcement** | `PdfExtractor.Extract`. |
| **Outcome** | `StopPage` resolved to `totalPages`. |
| **Source** | `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs` |

---

## BR-SFC-027 Descending range reverses page order

| Field | Value |
| --- | --- |
| **Statement** | A descending range (stop page lower than start page) emits the pages in reverse order. |
| **Trigger** | Per range, during extraction. |
| **Enforcement** | `PdfExtractor.Extract`. |
| **Outcome** | Reversed page sequence in the output. |
| **Source** | `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs` |

---

## BR-SFC-028 Extraction and merge failures propagate

| Field | Value |
| --- | --- |
| **Statement** | Extraction and merge failures are not swallowed: exceptions propagate to the caller (unlike single-document conversion). |
| **Trigger** | Syncfusion failure during extract/merge. |
| **Enforcement** | `PdfExtractor.Extract`; `PdfMerger.Merge`. |
| **Outcome** | Exception surfaces to the calling service. |
| **Source** | `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs`, `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-029 Merge performed at no conformance

| Field | Value |
| --- | --- |
| **Statement** | The merge itself is always performed at conformance `None`; PDF/A is applied only as a post-merge pass. |
| **Trigger** | Merge document creation. |
| **Enforcement** | `PdfMerger.MergeDocumentRanges`. |
| **Outcome** | Non-conformant intermediate document. |
| **Source** | `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-030 Whole file or extracted subset per range

| Field | Value |
| --- | --- |
| **Statement** | A document range contributes its whole file when it is a single, non-partial range; otherwise only the selected pages are extracted first. |
| **Trigger** | Per range, during merge. |
| **Enforcement** | `PdfMerger.MergeDocumentRanges`. |
| **Outcome** | Whole file or extracted subset added to the merge set. |
| **Source** | `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-031 PDF/A replaces plain PDF only on success

| Field | Value |
| --- | --- |
| **Statement** | The PDF/A version of a merged document replaces the plain PDF only when the conformance pass produced a non-empty byte array. |
| **Trigger** | PDF/A pass after merge. |
| **Enforcement** | `PdfMerger.MergeDocumentRanges`. |
| **Outcome** | `request.Options.OutputFormat` promoted to `PdfA` only on success. |
| **Source** | `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-032 PDF/A merge failure logged as warning

| Field | Value |
| --- | --- |
| **Statement** | A failure to produce PDF/A during merge is a logged warning, not an error: the plain PDF is returned instead. |
| **Trigger** | Exception in the PDF/A pass. |
| **Enforcement** | `PdfMerger.MergeDocumentRanges` `catch` block. |
| **Outcome** | `ILogService.LogException` called; merge still reports success. |
| **Source** | `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-033 Merge result always reported succeeded

| Field | Value |
| --- | --- |
| **Statement** | A merge result is always reported as succeeded. |
| **Trigger** | Merge completion. |
| **Enforcement** | `PdfMerger.MergeDocumentRanges`. |
| **Outcome** | `Status = PdfStatus.Succeeded` set unconditionally. |
| **Source** | `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

---

## BR-SFC-034 Form fields detected by non-null Form

| Field | Value |
| --- | --- |
| **Statement** | A PDF is considered to have form fields when the loaded document exposes a non-null `Form`. |
| **Trigger** | Form inspection. |
| **Enforcement** | `PdfHelper.HasFormFields`. |
| **Outcome** | Boolean result. |
| **Source** | `PdfApi.SyncFusionCore\PdfHelper.cs` |

---

## BR-SFC-035 Flatten makes fields non-editable

| Field | Value |
| --- | --- |
| **Statement** | Flattening makes form fields non-editable by setting `Form.Flatten` and re-saving. |
| **Trigger** | Flatten with a form present. |
| **Enforcement** | `PdfHelper.Flatten`. |
| **Outcome** | Flattened bytes returned. |
| **Source** | `PdfApi.SyncFusionCore\PdfHelper.cs` |

---

## BR-SFC-036 Flatten without form returns input unchanged

| Field | Value |
| --- | --- |
| **Statement** | Flattening a PDF without form fields returns the input unchanged. |
| **Trigger** | Flatten with no form. |
| **Enforcement** | `PdfHelper.Flatten`. |
| **Outcome** | Original bytes returned. |
| **Source** | `PdfApi.SyncFusionCore\PdfHelper.cs` |

---

## BR-SFC-037 Exception detail attributed to PdfApi

| Field | Value |
| --- | --- |
| **Statement** | Exception details raised from this library are always attributed to the application name `PdfApi`. |
| **Trigger** | Exception detail construction. |
| **Enforcement** | `PdfHelper.GetApplicationDetail`. |
| **Outcome** | `Application = "PdfApi"` on every emitted detail. |
| **Source** | `PdfApi.SyncFusionCore\PdfHelper.cs` |

---

## BR-SFC-038 Exception detail captures diagnostic context

| Field | Value |
| --- | --- |
| **Statement** | Exception details capture the environment track, machine name, OS user and local timestamp of the failure. |
| **Trigger** | Exception detail construction. |
| **Enforcement** | `PdfHelper.GetApplicationDetail`. |
| **Outcome** | Diagnostic context attached to the logged detail. |
| **Source** | `PdfApi.SyncFusionCore\PdfHelper.cs` |

---

## BR-SFC-039 Syncfusion license registered once per process

| Field | Value |
| --- | --- |
| **Statement** | The Syncfusion runtime must be licensed once per process before any Syncfusion API is used. |
| **Trigger** | Application start. |
| **Enforcement** | `LicenseHelper.LicenseSyncfusion`. |
| **Outcome** | `SyncfusionLicenseProvider.RegisterLicense` called with the embedded key. |
| **Source** | `PdfApi.SyncFusionCore\LicenseHelper.cs` |

---

## Notes
- All 39 rules are locally defined and enforced by types physically compiled into this library; no
  rule in this library is inherited from a base class or framework service.
- [`BR-SFC-038`](#br-sfc-038-exception-detail-captures-diagnostic-context)'s environment value is sourced from `IEnvironmentService.GetApplicationTrack()`
  (`SAIF.Framework.Core`), a dependency injected by the caller rather than created by this library.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [API reference](api/index.md)

## Sources
- `PdfApi.SyncFusionCore\Convert\ConverterFactory.cs`
- `PdfApi.SyncFusionCore\Convert\BaseConverter.cs`
- `PdfApi.SyncFusionCore\Convert\WordConverter.cs`
- `PdfApi.SyncFusionCore\Convert\ExcelConverter.cs`
- `PdfApi.SyncFusionCore\Convert\RtfConverter.cs`
- `PdfApi.SyncFusionCore\Convert\TxtConverter.cs`
- `PdfApi.SyncFusionCore\Convert\ImageConverter.cs`
- `PdfApi.SyncFusionCore\Convert\TiffConverter.cs`
- `PdfApi.SyncFusionCore\Convert\PdfConverter.cs`
- `PdfApi.SyncFusionCore\Convert\HtmlConverter.cs`
- `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs`
- `PdfApi.SyncFusionCore\Merge\PdfMerger.cs`
- `PdfApi.SyncFusionCore\PdfHelper.cs`
- `PdfApi.SyncFusionCore\LicenseHelper.cs`
